﻿using System;
using System.IO.Ports;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;

namespace HubertTestProgram
{
    /* Open and closing the port all the time might not be a good idea. .NET doc:
     * The best practice for any application is to wait for some amount of time
     * after calling the Close method before attempting to call the Open method,
     * as the port may not be closed instantly.
     * 
     * So, when we try to send two commands immediatley after one another, we call
     * Open just after Close...
     * 
     */
    public class SerialCommunication
    {

        #region Fields
        private SerialPort serialPort;
        private String portName;

        //Thread for communication
        private Thread ioThread;
        private double ioThreadSleepInterval;
        private bool running;
        private LinkedList<byte[]> buffer;
        #endregion

        #region Private methods
        private void Initialize()
        {
            this.serialPort = new SerialPort();
            serialPort.PortName = this.portName;
            serialPort.BaudRate = 9600;
            serialPort.DataBits = 8;
            serialPort.Parity = Parity.None;
            serialPort.StopBits = StopBits.One;
            serialPort.Open(); //Now opended only once
            SendCommandsFromBufferLoop();
        }

        private void SendCommandsFromBufferLoop()
        {
            while(running){
                if (buffer.Count > 0) //Buffer not empty
                {
                    byte[] data = buffer.First.Value;
                    serialPort.Write(data, 0, data.Length);
                    buffer.RemoveFirst();

                    if (buffer.Count > 50)
                    {
                        MessageBox.Show("Warning! Serial Output Buffer high:" + buffer.Count.ToString());
                    }
                }
                Thread.Sleep((int)(ioThreadSleepInterval * 1000));
            }
        }
        #endregion

        #region Public methods
        public SerialCommunication(string portName)
        {
            this.buffer = new LinkedList<byte[]>();
            //this.serialPort = new SerialPort();
            //Initialize(portName);
            this.portName = portName;
            this.running = true;
            this.ioThreadSleepInterval = 0.300;
            this.ioThread = new Thread(Initialize);
            this.ioThread.Start();
        }

        public bool TestConnection()
        {
            try
            {
                serialPort.Open();
                serialPort.Close();
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

        public void SendCommand(byte[] data)
        {
            buffer.AddLast(data);
        }

        public byte[] ReadData(int nrOfBytesToReceive)
        {
            //serialPort.Open();
            byte[] receivedData = new byte[nrOfBytesToReceive];
            for (int i = 0; i < nrOfBytesToReceive; i++)
            {
                receivedData[i] = (byte)serialPort.ReadByte();
            }
            //serialPort.Close();
            return receivedData;
        }

        public byte[] SendCommandAndReceive(byte[] sendData, int nrOfReceivedBytes)
        {
            //serialPort.Open();
            serialPort.Write(sendData, 0, sendData.Length);
            byte[] receivedData = new byte[nrOfReceivedBytes];
            for (int i = 0; i < nrOfReceivedBytes; i++)
            {
                receivedData[i] = (byte)serialPort.ReadByte();
            }

            //serialPort.Close();
            return receivedData;
        }

        public void DisposeConnection()
        {
            this.running = false;
            ioThread.Join();
            serialPort.Close();
            serialPort.Dispose();
        }
        #endregion

        #region Public static methods
        public static string[] getPortNames()
        {
            return SerialPort.GetPortNames();
        }
        #endregion

    }
}
